package com.example.petplanet.service;


import com.alibaba.fastjson.JSON;
import com.example.petplanet.domain.Content;
import com.example.petplanet.domain.LikeComment;
import com.example.petplanet.domain.LikeContent;
import com.example.petplanet.domain.User;
import com.example.petplanet.mapper.*;
import com.example.petplanet.util.CACHEUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class ShareService {

    private final RelationDao relationDao;
    private final MarkBookDao markBookDao;
    private final ContentDao contentDao;
    private final StringRedisTemplate stringRedisTemplate;
    private final LikeContentDao likeContentDao;
    private final LikeCommentDao likeCommentDao;


    private static final Set<Long> toDeleteMarkBookIds= new HashSet<>();

    private static final Set<Long> toUpdateLikeMarkUsers= new HashSet<>();


    @Autowired
    public ShareService(RelationDao relationDao, MarkBookDao markBookDao, ContentDao contentDao, StringRedisTemplate stringRedisTemplate, LikeContentDao likeContentDao, LikeCommentDao likeCommentDao) {
        this.relationDao = relationDao;
        this.markBookDao = markBookDao;
        this.contentDao = contentDao;
        this.stringRedisTemplate = stringRedisTemplate;
        this.likeContentDao = likeContentDao;
        this.likeCommentDao = likeCommentDao;
    }


    public Set<Long> getToUpdateLikeMarkUsers(){
        return toUpdateLikeMarkUsers;
    }

    public void addToUpdateLikeMarkUsers(Long userId){
        toUpdateLikeMarkUsers.add(userId);
    }

    public void emptyToUpdateLikeMarkUsers(){
        toUpdateLikeMarkUsers.clear();
    }

    public Set<Long> getToDeleteMarkBookIds(){
        return toDeleteMarkBookIds;
    }

    public void addToDeleteMarkBookIds(Long id){
        toDeleteMarkBookIds.add(id);
    }

    public void emptyToDeleteMarkBookIds(){
        toDeleteMarkBookIds.clear();
    }

    public void syncUserFollow(User user){
        Set<Long> redisFollows = user.getFollows();
        Long userId = user.getUserId();
        Set<Long> mysqlFollows = relationDao.getFollowsByUserId(userId);
        // 找出两个集合的共同部分
        Set<Long> commonFollows = new HashSet<>(redisFollows);
        commonFollows.retainAll(mysqlFollows);
        // 从redisFollows和mysqlFollows中去除共同部分
        redisFollows.removeAll(commonFollows);
        mysqlFollows.removeAll(commonFollows);
        // 将redisFollows中剩余的元素添加到mysql
        if (!redisFollows.isEmpty()) {
            relationDao.batchFollowUser(userId, new ArrayList<>(redisFollows));
        }
        // 将mysqlFollows中剩余的元素的状态置为0
        if (!mysqlFollows.isEmpty()) {
            relationDao.batchCancelFollow(userId, new ArrayList<>(mysqlFollows));
        }
    }

    public void syncUserMarkBook(User user){
        Set<Long> redisMarkBooks = user.getMarkBooks();
        Long userId = user.getUserId();
        Set<Long> mysqlMarkBooks = markBookDao.getMarkBooksByUserId(userId);

        // 找出两个集合的共同部分
        Set<Long> commonMarkBooks = new HashSet<>(redisMarkBooks);
        commonMarkBooks.retainAll(mysqlMarkBooks);
        // 从redisFollows和mysqlFollows中去除共同部分
        redisMarkBooks.removeAll(commonMarkBooks);
        mysqlMarkBooks.removeAll(commonMarkBooks);
        // 将redisMarkBooks中剩余的元素添加到mysql
        if (!redisMarkBooks.isEmpty()) {
            for (Long contentId:redisMarkBooks){
                Long targetId=getTargetId(contentId);
                if(targetId == null) continue;
                //如果在高并发下，redis未记录已删除的content_id，同步失败抛出异常，后续正常进行
                try {
                    markBookDao.markBook(userId,contentId,targetId);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        // 将mysqlMarkBooks中剩余的元素的状态置为0
        if (!mysqlMarkBooks.isEmpty()) {
            markBookDao.batchCancelMarkBook(userId, new ArrayList<>(mysqlMarkBooks));
        }
    }

    public void syncUserLike(Long userId){
        //点赞帖子
        Set<String> contentKeys = stringRedisTemplate.keys("app:like:content:"+userId+":*:*");
        List<LikeContent> likeContentsToUpdate = new ArrayList<>();
        if (!contentKeys.isEmpty())  {
            for (String key : contentKeys){

                String value=stringRedisTemplate.opsForValue().get(key);
                String[] parts = key.split(":");
                Long contentId = Long.parseLong(parts[4]);
                Long targetId = Long.parseLong(parts[5]);
                boolean status=Boolean.parseBoolean(value);
                LikeContent likeContent=new LikeContent();
                likeContent.setUserId(userId);
                likeContent.setContentId(contentId);
                likeContent.setTargetId(targetId);
                likeContent.setStatus(status);
                likeContentsToUpdate.add(likeContent);
            }
        }
        if (!likeContentsToUpdate.isEmpty()){
            likeContentDao.batchInsertLikeContent(likeContentsToUpdate);
        }
        //点赞评论
        Set<String> comKeys = stringRedisTemplate.keys("app:like:comment:"+userId+":*");
        List<LikeComment> likeCommentsToUpdate = new ArrayList<>();
        if(!comKeys.isEmpty()){
            for (String key : comKeys){
                String value=stringRedisTemplate.opsForValue().get(key);
                String[] parts = key.split(":");
                Long commentId = Long.parseLong(parts[4]);
                boolean status=Boolean.parseBoolean(value);
                LikeComment likeComment=new LikeComment();
                likeComment.setUserId(userId);
                likeComment.setCommentId(commentId);
                likeComment.setStatus(status);
                likeCommentsToUpdate.add(likeComment);
            }
        }
        if(!likeCommentsToUpdate.isEmpty()){
            likeCommentDao.batchInsertLikeComment(likeCommentsToUpdate);
        }
    }


    //获取帖子对应的用户id
    public Long getTargetId(Long contentId){
        String key="app:content:"+contentId;
        Boolean exists = stringRedisTemplate.hasKey(key);
        if (!exists) {
            // 如果键不存在，直接设置值
            Content content=contentDao.getContentById(contentId);
            if(content==null) return null;
            Long targetId=content.getUserId();
            stringRedisTemplate.opsForValue().set(key, targetId.toString(), 7, TimeUnit.HOURS);
        }
        String currentValue = stringRedisTemplate.opsForValue().get(key);
        return Long.parseLong(currentValue);
    }

    //获取用户redis数据
    public User getUserByIdRedis(Long id){
        String key="app:user:"+id.toString();
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);
        if(userMap.isEmpty()) return null;
        String userJson= (String) userMap.get("user");
        return JSON.parseObject(userJson, User.class);
    }


    //更新用户redis数据
    //关于用户的Redis不再次设置时间，需要手动设置
    public void updateUserRedis(User user){
        String key="app:user:"+user.getUserId().toString();
        String userJson=JSON.toJSONString(user);
        stringRedisTemplate.opsForHash().put(key, "user", userJson);
    }


    //判断用户是否在线
    public boolean judgeUserExistsRedis(Long userId){
        String key="app:user:"+userId.toString();
        return stringRedisTemplate.hasKey(key);
    }


    public boolean updateKeyValue(String key, String value, long timeout, TimeUnit timeUnit) {
        Boolean exists = stringRedisTemplate.hasKey(key);
        if (!exists) {
            // 如果键不存在，直接设置值
            stringRedisTemplate.opsForValue().set(key, value, timeout, timeUnit);
            return true;
        } else {
            // 如果键存在，获取当前值
            String currentValue = stringRedisTemplate.opsForValue().get(key);
            if (value.equals(currentValue)) {
                // 如果当前值已经是目标值，直接返回 false
                return false;
            } else {
                // 如果当前值不是目标值，更新值并重新设置过期时间
                stringRedisTemplate.opsForValue().set(key, value, timeout, timeUnit);
                return true;
            }
        }
    }


    //删除评论的redis数据，包括user的likeComments
    //当删除帖子时需要处理数据量过大，之后优化
    public void deleteCommentRedis(List<LikeComment> likeComments,Set<Long> commentIds){
        //删除已同步数据
        for (LikeComment likeComment : likeComments){
            Long userId = likeComment.getUserId();
            Long commentId = likeComment.getCommentId();
            if(judgeUserExistsRedis(userId)){
                //用户是否在线
                User user = getUserByIdRedis(userId);
                user.getLikeComments().remove(commentId);
                updateUserRedis(user);
            }
//            stringRedisTemplate.delete("app:like:comment:" + userId + ":" + commentId);
        }
        //删除未同步数据
        for (Long commentId :commentIds){
            String countKey="app:likeCount:comment:" + commentId.toString();
            if(stringRedisTemplate.hasKey(countKey)){
                stringRedisTemplate.delete(countKey);
                Set<String> NoSYncKey = stringRedisTemplate.keys("app:like:comment:*:"+ commentId);
                if (!NoSYncKey.isEmpty())  {
                    for (String key : NoSYncKey){
                        String[] parts = key.split(":");
                        Long noSyncUserId = Long.parseLong(parts[3]);
                        User noSyncUser = getUserByIdRedis(noSyncUserId);
                        noSyncUser.getLikeComments().remove(commentId);
                        updateUserRedis(noSyncUser);
                        stringRedisTemplate.delete(key);
                    }
                }
            }
        }
    }

    //查看是可删除时间
    public boolean isForbiddenDelTime() {
        LocalDateTime now = LocalDateTime.now();
        int hour = now.getHour();
        int minute = now.getMinute();
        return (hour + 1) % 6 == 0 && minute == 59;
    }

    //进行同步操作的相关锁检查
    public boolean isSyncLockCheckTime() {
        LocalDateTime now = LocalDateTime.now();
        int hour = now.getHour();
        int minute = now.getMinute();
        return hour % 6 == 0 && minute <= 5;
    }

}
